package com.fastfood.util;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.struts2.ServletActionContext;

import com.jolbox.bonecp.BoneCP;
import com.jolbox.bonecp.BoneCPConfig;
import com.mysql.jdbc.Statement;

public class DBConnectionPool {
	
	private static DBConnectionPool dbConnectionPool = new DBConnectionPool();
	
	private static String driverClass;
	private static String connectionUrl;
	private static String username;
	private static String password;
	
	private static BoneCP connectionPool;
	
	static {
		loadConfigProperties();
		createConnectionPool();
	}
	
	private DBConnectionPool() { }
	
	private static void loadConfigProperties() {
		Properties prop = new Properties();
		Path path = Paths.get(ServletActionContext.getServletContext().getRealPath(CommonConstants.DB_CONFIG_FILE));
		try {
			prop.load(Files.newInputStream(path, StandardOpenOption.READ));
			
			driverClass = prop.getProperty("driverClass");
			connectionUrl = prop.getProperty("connectionUrl");
			username = prop.getProperty("username");
			password = prop.getProperty("password");
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private static void createConnectionPool() {
		try {
			Class.forName(driverClass);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		BoneCPConfig config = new BoneCPConfig();
		config.setJdbcUrl(connectionUrl);
		config.setUsername(username);
		config.setPassword(password);
		
		/**
		 * In order to reduce lock contention and thus improve performance, each incoming connection request picks off
		 * a connection from a pool that has thread-affinity, i.e. pool[threadId % partition_count].
		 * The higher this number, the better your performance will be for the case when you have plenty of short-lived threads.
		 * Beyond a certain threshold, maintenence of these pools will start to have a negative
		 * effect on performance (and only for the case when connections on a partition start running out).
		 * Default: 1, minimum: 1, recommended: 3-4 (but very app specific)
		 */
		config.setPartitionCount(3);
		
		/**
		 * The number of connections to create per partition.
		 * Setting this to 5 with 3 partitions means you will have 15 unique connections to the database.
		 * Note that BoneCP will not create all these connections in one go but rather start off
		 * with minConnectionsPerPartition and gradually increase connections as required.
		 */
		config.setMaxConnectionsPerPartition(10);
		
		/**
		 * The number of connections to start off with per partition.
		 */
		config.setMinConnectionsPerPartition(5);
		
		/**
		 * When the available connections are about to run out, BoneCP will dynamically create new ones in batches.
		 * This property controls how many new connections to create in one go
		 * (up to a maximum of maxConnectionsPerPartition). Note: This is a per partition setting.
		 * Default: 10
		 */
		config.setAcquireIncrement(10);
		
		try {
			connectionPool = new BoneCP(config);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public static DBConnectionPool getConnectionPool() {
		if (connectionPool == null) {
			createConnectionPool();
		}
		return dbConnectionPool;
	}
	
	public Connection getConnection() {
		Connection conn = null;
		if (connectionPool != null) {
			try {
				conn = connectionPool.getConnection();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return conn;
	}
	
	public void closeConnection(Connection conn, Statement stmt, ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if (stmt != null) {
			try {
				stmt.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void closeConnectionPool() {
		if (connectionPool != null) {
			connectionPool.close();
		}
	}
}
